<template>
  <view class="chat-page">
    <view class="chat-header">
      <view class="back-btn" @tap="goBack">返回</view>
      <view class="header-center">
        <image class="avatar" :src="peer.avatar || defaultAvatar" mode="cover" />
        <view class="name">{{ peer.name || '用户聊天' }}</view>
      </view>
    </view>

    <scroll-view class="chat-messages" scroll-y :scroll-top="scrollTop">
      <view v-if="!messages.length && loaded" class="timestamp">暂无消息</view>
      <view
        v-for="m in messages"
        :key="m.id || m.createdAt + Math.random()"
        class="message"
        :class="m.senderId === currentUserId ? 'self' : 'other'"
      >
        <view class="message-avatar">
          <image
            class="msg-avatar"
            :src="m.senderId === currentUserId ? selfAvatar : peer.avatar || defaultAvatar"
            mode="cover"
          />
        </view>
        <view class="message-content">
          <view class="message-bubble">
            <text v-if="m.msgType === 'text'">{{ m.content }}</text>
            <image
              v-else-if="m.msgType === 'image'"
              :src="m.payloadUrl"
              mode="widthFix"
              style="max-width: 420rpx; border-radius: 8rpx"
            />
            <view
              v-else-if="m.msgType === 'file'"
              class="file-card"
              style="display: flex; flex-direction: column; align-items: flex-start; gap: 8rpx"
            >
              <view class="file-row" style="display: flex; align-items: center; gap: 12rpx">
                <image
                  class="file-thumb"
                  src="/static/fileImage.jpg"
                  alt="file"
                  style="width: 80rpx; height: 80rpx; border-radius: 6rpx"
                />
                <u-icon name="download" size="40" @click="downloadFile(m.payloadUrl, m.content)"></u-icon>
                <u-icon name="eye" size="40" @click="lookloadFile(m.payloadUrl, m.content)"></u-icon>
              </view>
              <view class="file-name" style="font-size: 24rpx; color: #555">
                {{ m.content }}
              </view>
            </view>
            <text v-else>
              {{ m.msgType }}: {{ m.payloadUrl || m.content }}
            </text>
          </view>
          <view class="message-time">{{ formatTime(m.createdAt) }}</view>
        </view>
      </view>
    </scroll-view>

    <view class="chat-input">
      <textarea
        class="input"
        v-model="inputText"
        placeholder="输入消息..."
        auto-height
        :maxlength="10000"
        @confirm="send"
        confirm-type="send"
      />
      <button class="send-btn" :disabled="!canSend" @tap="send">发送</button>
      <button class="plus-btn" @tap="openAttach">＋</button>
    </view>
  </view>
  
  <u-popup v-model="popShow" mode="bottom">
		<view style="height: 120rpx;display: flex; padding: 18rpx;" >
			<u-button type="primary" @tap="uploadLocalFile('image')">发送图片</u-button>
      <u-button type="success" @tap="uploadLocalFile('file')">发送文件</u-button>
		</view>
	</u-popup>

</template>

<script setup lang="ts">
import { ref, reactive, computed, onUnmounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import dayjs from 'dayjs'
import { getMessageHistory, sendMessage, type ChatMessage } from '@/api/chat/message'
import { createSingleSession } from '@/api/chat/session'
import { uploadFile } from '@/api/chat/file'
import { BASE_URL } from '@/config/env';
import { saveAs } from 'file-saver';
// mqtt（参考 XwChatView）
import mqtt from 'mqtt'
const defaultAvatar = '/static/logo.png'

const peer = reactive<{ id?: number|string; name?: string; avatar?: string }>({})
const sessionId = ref<number | null>(null)
const currentUserId = ref<number>(0)
const messages = ref<ChatMessage[]>([])
const page = ref(1)
const size = ref(30)
const loaded = ref(false)
const inputText = ref('')
const scrollTop = ref(0)
const pendingUploadUrl = ref<string | null>(null)
const action = ref(BASE_URL + '/api/chat/file/upload')
const file = ref(null)
const popShow = ref(false)


const uploadLocalFile = (type) => {
  let extensionMsg = ["*"];
  if("image" === type) {
    extensionMsg = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'ico']
  } else if("file" === type) {
    extensionMsg = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 
    'pdf', 'txt', 'zip', 'rar','md', 'config','java','excel','json',
    'js','css','html','ts']
  }
  uni.chooseImage({
    extension: extensionMsg,  
    success: (chooseImageRes) => {
      const tempFilePaths = chooseImageRes.tempFilePaths;
      const uploadTask = uni.uploadFile({
        url: action.value, //仅为示例，非真实的接口地址
        filePath: tempFilePaths[0],
        header: {"token": uni.getStorageSync("token")},
        name: 'file',
        formData: {
          'user': 'test'
        },
        success: (uploadFileRes) => {
          pendingUploadUrl.value = JSON.parse(uploadFileRes.data).data
          const tempFiles = chooseImageRes.tempFiles
          inputText.value = tempFiles[0].name
          send()
          popShow.value = false
        }
      });

      uploadTask.onProgressUpdate((res) => {
        if("file" === type) {
          console.log('上传进度' + res.progress);
          console.log('已经上传的数据长度' + res.totalBytesSent);
          console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);
      
          // 测试条件，取消上传任务。
          if (res.progress > 50 && res.progress < 52) {
            console.log('上传进度 progress = ' + res.progress);
          }
        }
      });
    },
    
});

}

const handleFileChange = (e: any) => {
  file.value = e.target.files[0];
}

const selfAvatar = computed(() => {
  try {
    const u = uni.getStorageSync('user')
    return u?.avatarUrl || defaultAvatar
  } catch { return defaultAvatar }
})

function goBack() {
  uni.navigateBack()
}

function getLoginUserId(): number {
  try {
    const u = uni.getStorageSync('user')
    return u?.id || 0
  } catch { return 0 }
}

function formatTime(t?: string) {
  if (!t) return ''
  try {
    return dayjs(t).format('MM-DD HH:mm')
  } catch {
    return t as any
  }
}

async function ensureSessionId() {
  if (sessionId.value) return
  if (!peer.id) return
  const me = currentUserId.value
  if (!me) return
  try {
    const res = await createSingleSession({ userA: me, userB: Number(peer.id) })
    const data = res?.data ?? res
    sessionId.value = data?.id || data?.data?.id || data?.data?.sessionId || sessionId.value
  } catch {
    // 已存在或失败可忽略；若 ChatTab 传入的 sessionId 存在则继续
  }
}

async function fetchMessages(reset = true) {
  if (!sessionId.value) return
  try {
    const res = await getMessageHistory({
      sessionId: sessionId.value,
      page: page.value,
      size: size.value,
    })
    const list: ChatMessage[] = (res?.data?.data || res?.data || []) as any
    const ordered = [...list].reverse()
    messages.value = reset ? ordered : [...messages.value, ...ordered]
    // 滚动到底部
    setTimeout(() => {
      scrollTop.value = Date.now()
    }, 0)
  } finally {
    loaded.value = true
  }
}

const canSend = computed(() => {
  return !!sessionId.value && !!currentUserId.value && (!!inputText.value.trim() || pendingUploadUrl.value !== null)
})



async function send() {
  if (!sessionId.value || !currentUserId.value) return
  if (!inputText.value.trim() && pendingUploadUrl.value === null) return

  const imageExts = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'ico']
  let msgType: string = 'text'
  let content: string | undefined = inputText.value.trim()
  let payloadUrl: string | undefined = undefined

  if (pendingUploadUrl.value !== null) {
    // 根据扩展名判断图片/文件
    const lower = decodeURIComponent(pendingUploadUrl.value).toLowerCase()
    const fileName = (lower.split('\\').pop() || lower.split('/').pop() || '').trim()
    const ext = (fileName.split('.').pop() || '').trim()
    msgType = imageExts.includes(ext) ? 'image' : 'file'
    payloadUrl = pendingUploadUrl.value || undefined
    // content 作为标题/文本
    content = inputText.value.trim() || fileName
  }

  const body: ChatMessage = {
    senderId: currentUserId.value,
    sessionId: sessionId.value || undefined,
    msgType,
    content,
    payloadUrl,
  }

  try {
    const res = await sendMessage({ targetSessionId: sessionId.value || undefined }, body)
    const ok = (res?.data?.code ?? res?.code) === 0 || !!res?.data?.data || !!res?.id
    if (ok) {
      inputText.value = ''
      pendingUploadUrl.value = null
      await fetchMessages(true)
    } else {
      uni.showToast({ title: '发送失败', icon: 'none' })
    }
  } catch (e:any) {
    uni.showToast({ title: e?.message || '发送失败', icon: 'none' })
  }
}

function openAttach() {
  popShow.value = true
}

function downloadFile(fileUrl, fileName) {

  uni.downloadFile({
    url: fileUrl,//下载文件的地址
    success: (res) => {
      if (res.statusCode === 200) {
        /** 
         *保存文件到本地
          *res.tempFilePath：下载好文件的临时路径，用来保存
          *this.uploadedFile.name：下载的文件名称
  */       
        saveAs(res.tempFilePath,fileName);
      }
    }
  });
}

function lookloadFile(url, fileName) {
  uni.openDocument({
    filePath: url,
    success: () => console.log('打开成功'),
    fail: (err) => console.error('打开失败', err)
  });
}


async function handleUploadByPickerFile(picked: any) {
  // picked.tempFiles: [{file, path, size, name, ...}]
  const first = picked?.tempFiles?.[0]
  const file: File | Blob | undefined = first?.file
  if (!file) {
    uni.showToast({ title: '未选择文件', icon: 'none' })
    return
  }
  try {
    const res = await uploadFile(file as any)
    const url = res?.data?.data || res?.data
    if (url) {
      pendingUploadUrl.value = String(url)
      inputText.value = first?.name || inputText.value
      await send()
    } else {
      uni.showToast({ title: '上传失败', icon: 'none' })
    }
  } catch (err:any) {
    uni.showToast({ title: err?.message || '上传失败', icon: 'none' })
  }
}

function onPickImage(e: any) {
  handleUploadByPickerFile(e)
}
function onPickFile(e: any) {
  handleUploadByPickerFile(e)
}

/** MQTT：进入聊天后启动并订阅当前会话（参考 XwChatView） */
const mqttClient = ref<any>(null)
function initMqttClient() {
  if (mqttClient.value) return mqttClient.value
  const url = (import.meta as any).env?.VITE_APP_WEBSOCKET_URL || 'ws://192.168.110.88:8083'
  try {
    const client = mqtt.connect(url, { reconnectPeriod: 3000 })
    client.on('connect', () => {
      console.log('[MQTT] connected', url)
      subscribeCurrentSession()
    })
    client.on('reconnect', () => {
      console.log('[MQTT] reconnecting...')
    })
    client.on('error', (err: any) => {
      console.error('[MQTT] error', err)
    })
    client.on('message', (topic: string, payload: any) => {
      try {
        const text = payload?.toString?.() || payload
        const msg = JSON.parse(text)
        if (topic === currentTopic()) {
          messages.value.push(msg)
          setTimeout(() => { scrollTop.value = Date.now() }, 0)
        }
      } catch {}
    })
    mqttClient.value = client
    return client
  } catch (e) {
    console.error('[MQTT] connect failed', e)
    return null
  }
}
function currentTopic() {
  return 'chat/group/' + (sessionId.value || '') + '/channel'
}
function subscribeCurrentSession() {
  const client = mqttClient.value
  if (!client || !sessionId.value) return
  const topic = currentTopic()
  client.subscribe(topic, (err: any) => {
    if (err) {
      console.error('[MQTT] subscribe error', topic, err)
    } else {
      console.log('[MQTT] subscribed', topic)
    }
  })
}
function cleanupMqtt() {
  try {
    const client = mqttClient.value
    if (client) {
      if (sessionId.value) {
        try { client.unsubscribe(currentTopic()) } catch {}
      }
      try { client.end(true) } catch {}
    }
  } catch {}
  mqttClient.value = null
}

onUnmounted(() => {
  cleanupMqtt()
})

onLoad((options: any) => {
  currentUserId.value = getLoginUserId()
  // URL 参数兜底
  if (options) {
    if (options.userId) peer.id = Number(options.userId)
    if (options.name) peer.name = options.name
    if (options.avatar) peer.avatar = options.avatar
  }
  // eventChannel 优先
  try {
    // @ts-ignore
    const ec = uni.getOpenerEventChannel && uni.getOpenerEventChannel()
    ec && ec.on && ec.on('user', (payload: any) => {
      const u = payload?.user || {}
      const s = payload?.session || {}
      peer.id = u?.id ?? peer.id
      peer.name = u?.nickname || u?.account || s?.name || peer.name
      peer.avatar = u?.avatarUrl || peer.avatar
      if (s?.id) sessionId.value = s.id
    })
  } catch {}

  // 确保有会话后，初始化 MQTT & 拉取消息
  Promise.resolve()
    .then(ensureSessionId)
    .then(() => {
      initMqttClient()
      // 若 MQTT 已连接且 sessionId 刚确定，补一次订阅
      subscribeCurrentSession()
      return fetchMessages(true)
    })
})
</script>

<style>
.chat-page {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: #f5f5f5;
}
.chat-header {
  position: relative;
  padding: 16rpx 24rpx;
  background: #ededed;
  border-bottom: 1px solid #d4d4d4;
  display: flex;
  align-items: center;
  justify-content: center; /* 居中头部内容 */
}
.back-btn {
  position: absolute;
  left: 16rpx;
  top: 50%;
  transform: translateY(-50%);
  color: #007aff;
  font-size: 28rpx;
}
.header-center {
  display: flex;
  align-items: center;
  justify-content: center;
}
.avatar {
  width: 72rpx;
  height: 72rpx;
  border-radius: 50%;
  background: #f0f0f0;
}
.name {
  margin-left: 16rpx;
  font-size: 32rpx;
  font-weight: 600;
  color: #333;
}
.chat-messages {
  flex: 1;
  overflow: auto;
}
.message {
  display: flex;
  align-items: flex-start;
  margin: 16rpx 0;
  padding: 0 8rpx; /* 内缩一点，避免右侧溢出 */
}
.message.self {
  flex-direction: row-reverse;
}
.message-avatar {
  width: 56rpx;
  height: 56rpx;
  border-radius: 8rpx;
  margin: 0 10rpx; /* 缩小边距，右侧头像更靠左 */
  flex-shrink: 0;
  overflow: hidden;
}
.msg-avatar {
  width: 100%;
  height: 100%;
  border-radius: 8rpx;
  background: #eee;
}
.message-content {
  max-width: 70%; /* 提高文本容纳，减少溢出 */
}
.message-bubble {
  padding: 14rpx 18rpx;
  border-radius: 8rpx;
  font-size: 28rpx;
  line-height: 1.5;
  box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}
.message.other .message-bubble {
  background: #ffffff;
  border: 1px solid #e6e6e6;
  margin-left: 8rpx;
}
.message.self .message-bubble {
  background: #9eea6a;
  color: #333;
  margin-right: 8rpx; /* 向左靠近，避免气泡太靠右 */
}
.message-time {
  font-size: 22rpx;
  color: #999;
  margin-top: 8rpx;
  text-align: left;
}
.message.self .message-time {
  text-align: right;
}
.timestamp {
  text-align: center;
  color: #999;
  font-size: 22rpx;
  margin: 20rpx 0;
}
.chat-input {
  padding: 12rpx 16rpx;
  background: #f5f5f5;
  border-top: 1px solid #e0e0e0;
  display: flex;
  gap: 12rpx;
  align-items: flex-end;
}
.chat-input-file {
    padding: 24rpx 28rpx;
    display: flex;
}
.plus-btn {
  padding: 10rpx 18rpx;
  margin-left: 10rpx;
  margin-right: 10rpx;
  border-radius: 8rpx;
  background: #eaeaea;
  color: #333;
  border: none;
  font-size: 28rpx;
}
.attach-sheet {
  background: #fff;
  padding: 20rpx;
  border-top-left-radius: 16rpx;
  border-top-right-radius: 16rpx;
}
.sheet-title {
  text-align: center;
  font-size: 28rpx;
  color: #333;
  margin-bottom: 16rpx;
}
.sheet-row {
  display: flex;
  justify-content: space-around;
  gap: 16rpx;
  margin-bottom: 12rpx;
}
.sheet-btn {
  padding: 16rpx 24rpx;
  border-radius: 10rpx;
  background: #f1f1f1;
  color: #333;
  border: none;
  font-size: 28rpx;
}
.sheet-cancel {
  width: 100%;
  padding: 16rpx 24rpx;
  border-radius: 10rpx;
  background: #fff;
  color: #007aff;
  border: 1px solid #e6e6e6;
  font-size: 28rpx;
}
.input {
  flex: 1;
  min-height: 88rpx;
  max-height: 240rpx;
  padding: 12rpx;
  border-radius: 8rpx;
  border: 1px solid #e6e6e6;
  background: #fff;
  font-size: 28rpx;
}
.send-btn {
  padding: 10rpx 24rpx;
  background-color: #07c160;
  color: white;
  border: none;
  border-radius: 8rpx;
  font-size: 28rpx;
}
.send-btn:disabled {
  background: #bdbdbd;
}
</style>